package com.lu.component.crypto.interceptor;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.reflect.Field;
import java.util.Map;

import com.lu.component.crypto.annotations.FieldCrypto;
import com.lu.component.crypto.exception.CryptoException;
import com.lu.component.crypto.utils.CryptoUtil;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

/**
 * mybatis加密拦截处理器
 *
 * @author: zhanglu
 * @date: 2023/9/20 19:55
 * @modify: 修改人信息, 修改内容 2023/9/20 19:55
 */
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class EncryptInterceptor implements Interceptor {

    public Object intercept(Invocation invocation) throws Throwable {
        try {
            Object[] args = invocation.getArgs();
            if (checkSqlCommand((MappedStatement) args[0])) {
                Object param = args[1];
                if (param instanceof Map) {
                    ((Map) param).forEach((key, value) -> {
                        if (Convert.toStr(key).startsWith("param")) {
                            encrypt(value);
                        }
                    });
                } else {
                    encrypt(param);
                }
            }
            return invocation.proceed();
        } catch (Exception e) {
            throw new CryptoException(e);
        }
    }

    private boolean checkSqlCommand(MappedStatement mappedStatement) {
        return SqlCommandType.UPDATE == mappedStatement.getSqlCommandType() || SqlCommandType.INSERT == mappedStatement.getSqlCommandType();
    }

    private void encrypt(Object parameterObject) {
        if (parameterObject != null) {
            Field[] fields = ReflectUtil.getFields(parameterObject.getClass());
            for (Field field : fields) {
                if (ObjectUtil.isNotNull(field.getAnnotation(FieldCrypto.class))) {
                    String value = Convert.toStr(ReflectUtil.getFieldValue(parameterObject, field));
                    if (CharSequenceUtil.isNotBlank(value)) {
                        ReflectUtil.setFieldValue(parameterObject, field, CryptoUtil.aesEncrypt(value));
                    }
                }
            }
        }
    }

}
